package com.chen.service.impl;

import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.chen.common.constant.MessageConstant;
import com.chen.common.utils.ThreadLocalUtil;
import com.chen.judge.codesandbox.model.JudgeResult;
import com.chen.mapper.*;
import com.chen.popj.dto.ContestDTO;
import com.chen.popj.dto.ContestProblemSubmitDTO;
import com.chen.popj.dto.ProblemSubmitDTO;
import com.chen.popj.entity.*;
import com.chen.popj.vo.ContestCodeSubmitVO;
import com.chen.popj.vo.ContestProblemVO;
import com.chen.popj.vo.ContestVO;
import com.chen.service.ContestLeaderboardService;
import com.chen.service.ContestService;
import com.chen.service.ProblemService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class ContestServiceImpl implements ContestService {

    @Autowired
    ContestMapper contestMapper;

    @Autowired
    ProblemService problemService;

    @Autowired
    SamplesMapper samplesMapper;

    @Autowired
    ProblemMapper problemMapper;

    @Autowired
    ProblemSubmitMapper problemSubmitMapper;

    @Autowired
    ContestCodeSubmitMapper contestCodeSubmitMapper;

    @Autowired
    ContestLeaderboardService contestLeaderboardService;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    /**
     * 创建比赛
     *
     * @param contestDTO
     * @return
     */
    @Override
    @Transactional
    public Integer createContest(ContestDTO contestDTO) {
        Contest contest = new Contest();
        BeanUtils.copyProperties(contestDTO, contest);
        List<Integer> contestProblemIds = contestDTO.getContestProblemIds();
        List<ContestProblem> contestProblemList = new ArrayList<>();
        LocalDateTime freezeTime = contest.getContestEndTime().minusHours(1);
//        System.out.println(freezeTime);
        contest.setFreezeTime(freezeTime);
        contestMapper.createContest(contest);
        Integer contestId = contest.getId();
        Map<String, Integer> ThreadLocalMap = ThreadLocalUtil.get();
        Integer userId = ThreadLocalMap.get("id");
        for (Integer contestProblemId : contestProblemIds) {
            Problem problem = problemService.getById(contestProblemId);
            ContestProblem contestProblem = ContestProblem.builder().userId(userId).problemId(contestProblemId).contestId(contestId).title(problem.getTitle()).acceptedNumber(0).submitNumber(0).build();
            contestProblemList.add(contestProblem);
        }
        if (contestProblemList.isEmpty()) {
            throw new RuntimeException(MessageConstant.PROBLEM_NOT_FAILED);
        }
        saveProblem(contestProblemList);
        return contestId;
    }

    /**
     * 根据比赛id获取比赛题目
     *
     * @param contestId
     * @return
     */
    @Override
    public ContestVO getContestInfo(Integer contestId) {
        Map<String, Object> threadLocalMap = ThreadLocalUtil.get();
        Integer userId = (Integer) threadLocalMap.get("id");
//        System.out.println("userId" + userId);
//        System.out.println("contestId:" + contestId);
        Contest contestInfo = contestMapper.getContestInfo(contestId);
        List<Integer> contestProblemIds = contestMapper.getContestProblem(contestId);
//        List<ContestProblem> problemList = problemService.queryProblemByContestIds(contestProblemIds, contestId);


        // 查询比赛题目，把数据库查到的题目放到redis中
        List<ContestProblem> problemList = new ArrayList<>();
        // JSON problemListJson = JSONUtil.parse(problemList);
        ListOperations<String, String> listOperations = stringRedisTemplate.opsForList();
        String contestIdString = String.valueOf(contestId);
        List<String> problemListString = listOperations.range(contestIdString, 0, -1);
//        System.out.println(problemListString);
        if (problemListString == null || problemListString.isEmpty()) {
            problemList = problemService.queryProblemByContestIds(contestProblemIds, contestId);
            for (ContestProblem contestProblem : problemList) {
                listOperations.rightPush(contestIdString, JSONUtil.toJsonStr(contestProblem));
            }
            stringRedisTemplate.expire(contestIdString, 1, TimeUnit.HOURS);

        } else {
            // 把redis里的problemList转换为 List<ContestProblem > 数据类型的数据
            // problemList = Collections.singletonList(JSONUtil.toBean(problemListString, ContestProblem.class));
            for (String problemJson : problemListString) {
                problemList.add(JSONUtil.toBean(problemJson, ContestProblem.class));
            }
        }

        for (ContestProblem contestProblem : problemList) {
            String contestCodeSubmitResult = contestCodeSubmitMapper.getContestCodeSubmitResult(userId, contestProblem.getProblemId(), contestId);
            contestProblem.setResult(contestCodeSubmitResult);
        }
        ContestVO contestVO = new ContestVO();
        BeanUtils.copyProperties(contestInfo, contestVO);
        contestVO.setContestProblemList(problemList);
//        System.out.println(contestVO);
        return contestVO;
    }

    /**
     * 保存比赛的题目
     *
     * @param contestProblemList
     */
    @Override
    public void saveProblem(List<ContestProblem> contestProblemList) {
        contestMapper.saveProblem(contestProblemList);
    }

    /**
     * 根据比赛id获取比赛题目
     *
     * @param contestId
     * @return
     */
    @Override
    public List<Integer> getContestProblem(Integer contestId) {
        return contestMapper.getContestProblem(contestId);
    }

    /**
     * 根据比赛id和题目id获取比赛题目信息
     *
     * @param problemId
     * @param contestId
     * @return
     */
    @Override
    public ContestProblemVO getContestProblemDetails(Integer problemId, Integer contestId) {
        String key = contestId + ":" + problemId;
        ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();
        String problemInfoString = opsForValue.get(key);
        if (problemInfoString != null) {
            return JSONUtil.toBean(problemInfoString, ContestProblemVO.class);
        }
        List<Samples> samplesList = samplesMapper.queryById(problemId);
        Problem problem = problemMapper.getById(problemId);
        ContestProblem contestProblem = contestMapper.getContestProblemDetails(problemId, contestId);
        ContestProblemVO contestProblemVO = ContestProblemVO.builder().title(problem.getTitle()).submitNumber(contestProblem.getSubmitNumber()).acceptedNumber(contestProblem.getAcceptedNumber()).description(problem.getDescription()).inputDescription(problem.getInputDescription()).outputDescription(problem.getOutputDescription()).samplesList(samplesList).timeLimit(problem.getTimeLimit()).memoryLimit(problem.getMemoryLimit()).grade(problem.getGrade()).build();
        JSON json = JSONUtil.parse(contestProblemVO);
        opsForValue.set(key, String.valueOf(json), 60, TimeUnit.MINUTES);
        return contestProblemVO;
    }

    /**
     * 比赛题目提交题目代码
     *
     * @param contestProblemSubmitDTO
     * @return
     */
    @Override
    public Integer ContestProblemSubmit(ContestProblemSubmitDTO contestProblemSubmitDTO) {
        ProblemSubmitDTO problemSubmitDTO = ProblemSubmitDTO.builder().language(contestProblemSubmitDTO.getLanguage()).code(contestProblemSubmitDTO.getCode()).problemId(contestProblemSubmitDTO.getProblemId()).build();
        Integer problemSubmitId = problemService.problemSubmit(problemSubmitDTO, contestProblemSubmitDTO.getContestId());
//        ProblemSubmit problemSubmit = problemSubmitMapper.getById(problemSubmitId);
//        Map<String, Object> threadLocalMap = ThreadLocalUtil.get();
//        Integer userId = (Integer) threadLocalMap.get("id");
//        String judgeInfo = problemSubmit.getJudgeInfo();
//        JudgeResult judgeResult = JSONUtil.toBean(judgeInfo, JudgeResult.class);
//        ContestCodeSubmit contestCodeSubmit = contestCodeSubmitMapper.getByProblemSubmitId(problemSubmitId);
//        Short isFirstAc = contestCodeSubmit.getIsFirstAc();
//        ContestCodeSubmit contestCodeSubmitUpdate = new ContestCodeSubmit();
//        if ("Accepted".equals(judgeResult.getMessage())) {
//            if (isFirstAc == 0)
//                contestCodeSubmitUpdate.setIsFirstAc((short) 1);
//        } else {
//            Integer wrongCount = contestCodeSubmitUpdate.getWrongCount();
//            contestCodeSubmitUpdate.setWrongCount(wrongCount + 1);
//        }
//        contestCodeSubmitUpdate.setId(problemSubmit.getContestCodeId());
//        contestCodeSubmitUpdate.setUserId(userId);
//        contestCodeSubmitUpdate.setProblemId(contestProblemSubmitDTO.getProblemId());
//        contestCodeSubmitUpdate.setCode(contestCodeSubmit.getCode());
//        contestCodeSubmitUpdate.setSubmitTime(problemSubmit.getCreateTime());
//        contestCodeSubmitUpdate.setResult(judgeResult.getMessage());
//        contestCodeSubmitUpdate.setProblemSubmitId(problemSubmitId);
//        contestCodeSubmitMapper.update(contestCodeSubmitUpdate);
//        return contestCodeSubmitUpdate.getId();
        return problemSubmitId;
    }


    /**
     * 比赛时判玩题补充做题信息
     *
     * @param problemSubmitId
     * @return
     */
    @Transactional
    public Integer executeContestProblem(Integer problemSubmitId) {
        ProblemSubmit problemSubmit = problemSubmitMapper.getById(problemSubmitId);
        String judgeInfo = problemSubmit.getJudgeInfo();
        JudgeResult judgeResult = JSONUtil.toBean(judgeInfo, JudgeResult.class);
        ContestCodeSubmit contestCodeSubmit = contestCodeSubmitMapper.getByProblemSubmitId(problemSubmitId);
        Short isFirstAc = contestCodeSubmit.getIsFirstAc();
        ContestCodeSubmit contestCodeSubmitUpdate = new ContestCodeSubmit();
        if ("Accepted".equals(judgeResult.getMessage())) {
            if (isFirstAc == 0) contestCodeSubmitUpdate.setIsFirstAc((short) 1);
        } else {
            Integer wrongCount = contestCodeSubmitUpdate.getWrongCount();
            if (wrongCount == null) wrongCount = 0;
            contestCodeSubmitUpdate.setWrongCount(wrongCount + 1);
        }
        contestCodeSubmitUpdate.setId(problemSubmit.getContestCodeId());
        contestCodeSubmitUpdate.setUserId(problemSubmit.getUserId());
        contestCodeSubmitUpdate.setProblemId(problemSubmit.getProblemId());
        contestCodeSubmitUpdate.setCode(contestCodeSubmit.getCode());
        contestCodeSubmitUpdate.setSubmitTime(problemSubmit.getSubmitTime());
        contestCodeSubmitUpdate.setResult(judgeResult.getMessage());
        contestCodeSubmitUpdate.setProblemSubmitId(problemSubmitId);
        contestCodeSubmitUpdate.setSubmitTime(LocalDateTime.now());
        contestCodeSubmitMapper.update(contestCodeSubmitUpdate);
//        System.out.println(contestCodeSubmitUpdate);
        if ("Accepted".equals(contestCodeSubmitUpdate.getResult())) {
            ContestProblem contestProblem = ContestProblem.builder().problemId(contestCodeSubmitUpdate.getProblemId()).contestId(contestCodeSubmit.getContestId()).acceptedNumber(1).build();
//            System.out.println(contestProblem);
            contestMapper.updateByContestIdAndProblemId(contestProblem);
//            System.out.println("题目通过执行了通过次数加一");
        }
        return contestCodeSubmitUpdate.getId();
    }

    /**
     * 根据比赛id和用户id获取用户比赛时的代码提交记录
     *
     * @param contestId
     * @param userId
     * @return
     */
    @Override
    public List<ContestCodeSubmitVO> getSubmissionRecord(Integer contestId, Integer userId) {
        List<ContestCodeSubmit> contestCodeSubmitList = contestMapper.getSubmissionRecord(contestId, userId);
        List<ContestCodeSubmitVO> contestCodeSubmitVOList = new ArrayList<>();
        for (ContestCodeSubmit contestCodeSubmit : contestCodeSubmitList) {
            ContestCodeSubmitVO codeSubmitVO = ContestCodeSubmitVO.builder().id(contestCodeSubmit.getId()).title(contestCodeSubmit.getTitle()).result(contestCodeSubmit.getResult()).language(contestCodeSubmit.getLanguage()).submitTime(contestCodeSubmit.getSubmitTime()).code(contestCodeSubmit.getCode()).build();
            contestCodeSubmitVOList.add(codeSubmitVO);
        }
        return contestCodeSubmitVOList;
    }

    /**
     * 用户报名比赛
     *
     * @param contestId
     */
    @Override
    public void contestRegister(Integer contestId) {
        Map<String, Object> threadLocalUtil = ThreadLocalUtil.get();
        Integer userId = (Integer) threadLocalUtil.get("id");
        contestLeaderboardService.contestRegister(contestId, userId);
    }
}
